#pragma once
#include"Buffer.hpp"
#include"Util.hpp"
#include"HttpRequest_Response.hpp"
//Http接收状态
typedef enum {
    RECV_HTTP_ERROR,
    RECV_HTTP_LINE,
    RECV_HTTP_HEAD,
    RECV_HTTP_BODY,
    RECV_HTTP_OVER
} HttpRecvStatus;


//请求上下文（记录Http请求和接受的进度）
class HttpContext {
    private:
        int _response_status;//响应状态码
        HttpRecvStatus _recv_status;//Http接收状态
        HttpRequest _request;//已经解析到的请求信息
    private:
        bool ParseHttpLine(const std::string &line)//解析HTTP请求行
        {
            // exp: "GET /www.google.com/search?q=regex&limit=10 HTTP/1.1\r\n"
            //解析HTTP请求行
            std::regex e(
                "(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?",
                std::regex::icase//忽略大小写
            );
            /*  () 表⽰捕捉符合括号内格式的数据
             GET|HEAD|POST...   |表⽰或，也就是匹配这⼏个字符串中的任意⼀个
            .* 中.表⽰匹配除换⾏外的任意单字符, *表⽰匹配前边的字符任意次; 合起来在这⾥就是表⽰空格后匹配任意字符
            最终合并起来表⽰匹配以GET或者POST或者PUT...⼏个字符串开始，然后后边有个空格的字符串, 并在匹配成功后捕捉前边的请求⽅法字符串

            最外层的() 表⽰捕捉提取括号内指定格式的内容
            * ([^?]+)  [^xyz] 负值匹配集合，指匹配⾮^之后的字符， ⽐如[^abc] 则plain就匹配到plin字符
            * +匹配前⾯的⼦表达式⼀次或多次
            * 合并合并起来就是匹配⾮?字符⼀次或多次
            */
            std::smatch matches;
            bool ret = std::regex_match(line, matches, e);
            if(!ret) {
                _recv_status = RECV_HTTP_ERROR;
                _response_status = 400;//Bad Request
                return false;
            }
            // match[0]GET /www.google.com/search?q=regex&limit=10 HTTP/1.1
            // match[1]GET
            // match[2]/www.google.com/search
            // match[3]q=regex&limit=10
            // match[4]HTTP/1.1
            _request._method = matches[1];//获取请求方法
            std::transform(_request._method.begin(), _request._method.end(), _request._method.begin(), ::toupper);//将请求方法转为大写
            //资源路径获取需要url解码，但不需要将空格替换为+
            _request._path = Util::DecodeUrl(matches[2],false);//获取请求路径
            //协议版本获取
            _request._version = matches[4];//获取协议版本
            //请求参数获取
            std::vector<std::string> params_array;
            Util::Split(matches[3],"&",&params_array);
            for(auto &param : params_array){
                size_t pos = param.find('=');
                if(pos == std::string::npos) {
                    _recv_status = RECV_HTTP_ERROR;
                    _response_status = 400;//Bad Request
                    return false;
                }
                std::string key = param.substr(0,pos);
                std::string value = param.substr(pos+1);
                // _request._params[key] = value;
                _request.SetParam(key,value);
            }
            return true;
        }
        
        #define MAX_HTTP_LINE_SIZE 1024*8
        bool RecvAndParseHttpLine(Buffer *buf)//接受并解析HTTP请求行
        {
            if(_recv_status != RECV_HTTP_LINE)  return false;
            //1. 获取⼀⾏数据，带有末尾的换⾏
            std::string line = buf->GetLine();
            if(line.empty()){//没有能找到换行符
                if(buf->ReadableSize() > MAX_HTTP_LINE_SIZE){
                    _recv_status = RECV_HTTP_ERROR;
                    _response_status = 414;//请求URI过长
                    return false;
                }
                //缓冲区中数据不⾜⼀⾏，但是也不多，就等等新数据的到来
                return true;
            }
            if(line.size() > MAX_HTTP_LINE_SIZE){
                _recv_status = RECV_HTTP_ERROR;
                _response_status = 414;//请求URI过长
                return false;
            }
            if(ParseHttpLine(line)){
                _recv_status = RECV_HTTP_HEAD;//⾸⾏处理完毕，进⼊头部获取阶段
                return true;
            }   
            return false;
        }
        
        bool ParseHttpHead(const std::string &Line)//解析HTTP报头
        {
            //key: val\r\nkey: val\r\n....
            std::string line(Line);
            if(line.back() == '\n') line.pop_back();
            if(line.back() == '\r') line.pop_back();
            int pos = line.find(": ");
            if(pos == std::string::npos){
                _recv_status = RECV_HTTP_ERROR;
                _response_status = 400;//Bad Request
                return false;
            }
            std::string key = line.substr(0,pos);
            std::string value = line.substr(pos+2);
            _request.SetHeader(key,value);
            return true;
        }
        bool RecvAndParseHttpHead(Buffer *buf)//接受并解析HTTP报头
        {
            if(_recv_status != RECV_HTTP_HEAD)  return false;
            //⼀⾏⼀⾏取出数据，直到遇到空⾏为⽌， 头部的格式 key: val\r\nkey:val\r\n....
            while(true){
                std::string line = buf->GetLine();
                //缓冲区不足一行，或者一行数据太大
                if(line.empty()){//没能找到换行符
                    if(buf->ReadableSize() > MAX_HTTP_LINE_SIZE){
                        _recv_status = RECV_HTTP_ERROR;
                        _response_status = 413;//请求实体太大
                        return false;
                    }
                    //缓冲区中数据不⾜⼀⾏，但是也不多，就等等新数据的到来
                    return true;
                }
                if(line.size() > MAX_HTTP_LINE_SIZE){
                    _recv_status = RECV_HTTP_ERROR; 
                    _response_status = 413;//请求实体太大
                    return false;
                }else if(line == "\r\n" || line == "\n"){
                    break;
                }
                bool ret = ParseHttpHead(line);//解析头部
                if(!ret){
                    return false;
                }
                
            }
            _recv_status = RECV_HTTP_BODY;//进⼊请求体获取阶段
            return true;
        }

        bool RecvHttpBody(Buffer *buf)//接受并解析HTTP请求体
        {
            if(_recv_status != RECV_HTTP_BODY)  return false;
            //1. 获取请求体大小
            size_t content_length = _request.BodyLength();
            if(content_length == 0){
                _recv_status = RECV_HTTP_OVER;
                return true;
            }
            //2. 当前已经接收了多少正⽂,其实就是往  _request._body 中放了多少数据了
            size_t need_recv_len = content_length - _request._body.size();//还需要接收多少数据
            //3. 接收正⽂放到body中，但是也要考虑当前缓冲区中的数据，是否是全部的正⽂
            if(buf->ReadableSize() >= need_recv_len){//缓冲区中有足够的数据
                _request._body.append((char *)buf->GetReadPointer(),need_recv_len);
                buf->MoveReadIndex(need_recv_len);
                _recv_status = RECV_HTTP_OVER;
                return true;
            }else{//缓冲区中没有足够的数据,数据不⾜，取出数据，然后等待新数据到来
                _request._body.append((char *)buf->GetReadPointer(),buf->ReadableSize());
                buf->MoveReadIndex(buf->ReadableSize());
                return true;
            }
            return false;
        }
    public:
        HttpContext():_response_status(200),_recv_status(RECV_HTTP_LINE){}
        void ReSet()
        {
            _response_status = 200;
            _recv_status = RECV_HTTP_LINE;
            _request.ReSet();
        }
        int ResponseStatus() { return _response_status; }
        HttpRecvStatus RecvStatus() { return _recv_status; }
        HttpRequest &Request() { return _request; }
        
        //接收并解析HTTP请求
        void RecvAndPraseRequest(Buffer *buf)
        {
            //不同的状态，做不同的事情，但是这⾥不要break         
            // 因为处理完请求⾏后，应该⽴即处理头部，报头处理完后接受正文，⽽不是退出等新数据
            switch(_recv_status){
                case RECV_HTTP_LINE:
                    RecvAndParseHttpLine(buf);
                case RECV_HTTP_HEAD:
                    RecvAndParseHttpHead(buf);
                case RECV_HTTP_BODY:
                    RecvHttpBody(buf);
            }
            return;
        }
};

